/**
 * 
 * 使用下面描述的算法可以扰乱字符串 s 得到字符串 t ：
 * 1. 如果字符串的长度为 1 ，算法停止
 * 2. 如果字符串的长度 > 1 ，执行下述步骤：
 * 在一个随机下标处将字符串分割成两个非空的子字符串。即，如果已知字符串 s ，则可
 * 以将其分成两个子字符串 x 和 y ，且满足 s = x + y 。
 * 随机 决定是要「交换两个子字符串」还是要「保持这两个子字符串的顺序不变」。即，
 * 在执行这一步骤之后，s 可能是 s = x + y 或者 s = y + x 。
 * 在 x 和 y 这两个子字符串上继续从步骤 1 开始递归执行此算法。
 * 给你两个 长度相等 的字符串 s1 和 s2，判断 s2 是否是 s1 的扰乱字符串。如果是，返回 true
 * 否则，返回 false 。
 */

class Solution {
private:
    //-1表示false,1表示true,0表示未计算
    int memo[30][30][31];
    string s1,s2;
public:
    bool checkIfSimilar(int i1,int i2,int length){
        unordered_map<int,int>freq;
        for(int i = i1;i < i1 + length;i++){
            freq[s1[i]]++;
        }
        for(int i = i2;i < i2 + length;i++){
            freq[s2[i]]--;
        }
        if(any_of(freq.begin(),freq.end(),[](const auto& entry){
            return entry.second != 0;
        })){
            return false;
        }
        return true;
    }
    bool dfs(int i1,int i2,int length){
        if(memo[i1][i2][length]){
            return memo[i1][i2][length] == 1;
        }
        if(s1.substr(i1,length) == s2.substr(i2,length)){
            memo[i1][i2][length] = 1;
            return true;
        }
        if(!checkIfSimilar(i1,i2,length)){
            memo[i1][i2][length] = -1;
            return false;
        }
        for(int i = 1;i <length;i++){
            if(dfs(i1,i2,i) && dfs(i1 + i,i2 + i,length - i)){
                memo[i1][i2][length] = 1;
                return true;
            }
            //交换的情况
            if(dfs(i1,i2 + length - i,i) && dfs(i1 + i,i2,length - i)){
                memo[i1][i2][length] = 1;
                return true;
            }
        }
        memo[i1][i2][length] = -1;
        return false;
    }
    bool isScramble(string s1, string s2) {
        memset(memo,0,sizeof(memo));
        this->s1 = s1;
        this->s2 = s2;
        return dfs(0,0,s1.size());
    }
};